package com.mlog.njyjy.ndop.share.zabbix.request.core;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.gson.*;
import com.mlog.njyjy.ndop.share.zabbix.response.ZabbixResponse;
import com.mlog.njyjy.ndop.share.zabbix.response.ZabbixResponseError;
import com.mlog.njyjy.ndop.share.zabbix.support.Param;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * .
 * </p>
 *
 * @author <a href="mailto:yingxiufeng@mlogcn.com">alex</a>
 * @version v1.0, 2020/7/20
 */
public abstract class AbstractZabbixRequest<T extends ZabbixResponse> implements ZabbixRequest<T> {

    private final Logger log = LoggerFactory.getLogger(AbstractZabbixRequest.class);

    private int requestId = 1;

    /**
     * jsonrpc
     *
     * @return
     */
    @Override
    public String getJsonrpc() {
        return "2.0";
    }

    /**
     * request id
     *
     * @return id
     */
    @Override
    public int getId() {
        return requestId;
    }


    /**
     * 根据注解{@link com.mlog.njyjy.ndop.share.zabbix.support.Param}
     * 组织相应参数
     *
     * @return
     */
    protected Map<String, Object> getParamsByDefault() {
        List<Field> fields = getFields(this.getClass());
        Map<String, Object> params = new HashMap<>(fields.size());
        fields.forEach(field -> {
            Param param = field.getAnnotation(Param.class);
            String name = field.getName();
            if (param != null && StrUtil.isNotBlank(param.value())) {
                name = param.value();
            }
            Object fieldValue = ReflectUtil.getFieldValue(this, field);
            if (fieldValue != null) {
                params.put(name, fieldValue);
            }
        });
        return params;
    }

    /**
     * create new response
     *
     * @param result
     * @param responseBody
     * @param statusCode
     * @param reasonPhrase
     * @return
     */
    protected T createNewResponse(T result, String responseBody, int statusCode, String reasonPhrase) {
        JsonObject jsonMap = parseResponseBody(responseBody);
        result.setResponseCode(statusCode);
        result.setJsonString(responseBody);
        result.setJsonObject(jsonMap);

        boolean httpSucceeded = (statusCode / 100) == 2;
        if (httpSucceeded) {
            log.debug("[zabbix client] Request and operation succeeded");
            result.setId(jsonMap.getAsJsonPrimitive("id").getAsInt());
            result.setJsonrpc(jsonMap.getAsJsonPrimitive("jsonrpc").getAsString());
            if (jsonMap.has("error") && !jsonMap.get("error").isJsonNull()) {
                JsonObject error = jsonMap.getAsJsonObject("error");
                result.setError(new ZabbixResponseError().setCode(error.getAsJsonPrimitive("code").getAsInt())
                        .setMessage(error.getAsJsonPrimitive("message").getAsString())
                        .setData(error.getAsJsonPrimitive("data").getAsString()));
            }
        } else {
            result.setErrorCode(statusCode);
            if (result.getErrorMessage() == null) {
                result.setErrorMessage(statusCode + " " + (reasonPhrase == null ? "null" : reasonPhrase));
            }
            log.warn("[zabbix client] Response is failed. errorMessage is " + result.getErrorMessage());
        }
        return result;
    }

    private JsonObject parseResponseBody(String responseBody) {
        if (responseBody == null || responseBody.trim().isEmpty()) {
            return new JsonObject();
        }
        JsonElement parsed = new JsonParser().parse(responseBody);
        if (parsed.isJsonObject()) {
            return parsed.getAsJsonObject();
        } else {
            throw new JsonSyntaxException("Response did not contain a JSON Object");
        }
    }

    private List<Field> getFields(Class<?> beanClass) throws SecurityException {
        Assert.notNull(beanClass);
        Field[] allFields = new Field[]{};
        Class<?> searchType = beanClass;
        Field[] declaredFields;
        while (searchType != null) {
            declaredFields = searchType.getDeclaredFields();
            if (null == allFields) {
                allFields = declaredFields;
            } else {
                allFields = ArrayUtil.append(allFields, declaredFields);
            }
            searchType = searchType.getSuperclass();
        }
        // skip private
        return Arrays.stream(allFields)
                .filter(field -> field.getModifiers() == 1 || field.getModifiers() == 4)
                .collect(Collectors.toList());
    }
}
